#include "sdkmanager.h"

#include <iostream>

#include <QApplication>
#include <QMetaObject>
#include <QThread>

#include "rendermanager.h"

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
SdkManager::SdkManager() {}

//--------------------------------------------------------------------------------------------------
// 投递到主线程队列
//--------------------------------------------------------------------------------------------------
void SdkManager::PostToMainThread(std::function<void()> func)
{
    QMetaObject::invokeMethod(qApp, [func]() { func(); }, Qt::QueuedConnection);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
template<typename R>
R PostToMainThreadSync(std::function<R()> func)
{
    R result;
    QMetaObject::invokeMethod(
        qApp,
        [&result, &func]() { result = func(); },
        Qt::BlockingQueuedConnection
    );
    return result;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::Init()
{
    m_client = CreateP2PMSClient();
    if (!m_client) {
        std::cout << "Create p2pms client failed";
        return false;
    }

    if (ERR_OK != m_client->Init(this, this, "ws://47.97.17.103:8000/")) {
        std::cout << "P2PMSClient 初始化失败";
        return false;
    }

    std::cout << "P2PMSClient 初始化成功";
    return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<MediaDev> SdkManager::GetDevList(p2pms::MediaDeviceType dev_type)
{
    std::vector<MediaDev> dev_list;

    std::vector<MediaDevice> devices;
    m_client->GetDeviceManager()->GetMediaDevList(dev_type, devices);
    for (const auto &device : devices) {
        dev_list.push_back(MediaDev{device.id, device.name});
    }

    return dev_list;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<MediaDev> SdkManager::GetAudioInputDevList()
{
    return GetDevList(p2pms::MediaDeviceType::AudioInput);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<MediaDev> SdkManager::GetVideoInputDevList()
{
    return GetDevList(p2pms::MediaDeviceType::VideoInput);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<MediaDev> SdkManager::GetAudioOutputDevList()
{
    return GetDevList(p2pms::MediaDeviceType::AudioOutput);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::Login(const std::string& user_id)
{
    m_user_id = user_id;

    return ERR_OK == m_client->Login(user_id) ? true : false;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
std::vector<std::string> SdkManager::GetUserList()
{
    return m_client->GetUserList();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StartPublishVideo(const MediaDev& device, MediaInfo& media)
{
    p2pms::MediaDevice dev;
    dev.id = device.id;
    dev.name = device.name;
    dev.type = MediaDeviceType::VideoInput;

    return m_client->StartPublish(dev, media) == ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StopPublishVideo(const MediaInfo& media)
{
    return m_client->StopPublish(media) == ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StartPublishAudio(const MediaDev& device, MediaInfo& media)
{
    p2pms::MediaDevice dev;
    dev.id = device.id;
    dev.name = device.name;
    dev.type = MediaDeviceType::AudioInput;

    return m_client->StartPublish(dev, media) == ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StopPublishAudio(const MediaInfo& media)
{
    return m_client->StopPublish(media) == ERR_OK;
}


//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StartSubscribe(const std::string& user_id, const MediaInfo& media)
{
    return m_client->StartSubscribe(user_id, media) == ERR_OK;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnLoginResult(bool result)
{
    PostToMainThread([=] {
        if (m_login_result_handler)
			m_login_result_handler(result);
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnLoginNotify(const std::string& user_id)
{
    PostToMainThread([this, user_id] {
		if (m_user_add_handler)
			m_user_add_handler(user_id);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnLogoutNotify(const std::string& user_id)
{
    PostToMainThread([this, user_id] {
		if (m_user_remove_handler)
			m_user_remove_handler(user_id);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::OnIncomingCall(const std::string& call_type, const std::string& user_id,
                                const std::string& call_id)
{
    std::cout << "incoming call:" << call_id << " from user:" << user_id << std::endl;

	return PostToMainThreadSync<bool>([this, call_type, user_id, call_id]() {
        if (m_incoming_call_handler) {
            return m_incoming_call_handler(call_type, user_id, call_id);
        } else {
            return false;
        }
	});
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnCallStateChanged(const std::string& call_id, CallState state)
{
    PostToMainThread([this, call_id, state] {
        if (m_call_state_changed_handler)
            m_call_state_changed_handler(call_id, state);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStartPublishResult(const MediaInfo& media, bool result)
{
    PostToMainThread([this, media, result] {
		if (m_start_publish_result_handler)
			m_start_publish_result_handler(media, result);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStopPublishResult(const MediaInfo& media, bool result)
{
    PostToMainThread([this, media, result] {
		if (m_stop_publish_result_handler)
			m_stop_publish_result_handler(media, result);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStartSubscribeResult(const std::string& user_id, const MediaInfo& media, 
	bool result)
{
    PostToMainThread([this, user_id, media, result] {
		if (m_start_subscribe_result_handler)
			m_start_subscribe_result_handler(user_id, media, result);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStopSubscribeResult(const std::string& user_id, const MediaInfo& media, 
	bool result)
{
    PostToMainThread([this, user_id, media, result] {
		if (m_stop_subscribe_result_handler)
			m_stop_subscribe_result_handler(user_id, media, result);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStartPublish(const std::string& user_id, const p2pms::MediaInfo& media)
{
    std::cout << "OnStartPublish, user_id:" << user_id.c_str();

    PostToMainThread([this, user_id, media] {
		if (m_start_publish_notify_handler)
			m_start_publish_notify_handler(user_id, media);
    });
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnStopPublish(const std::string& user_id, const p2pms::MediaInfo& media)
{
    std::cout << "OnStopPublish, user_id:" << user_id.c_str();

    PostToMainThread([this, user_id, media] {
        if (m_stop_publish_notify_handler)
            m_stop_publish_notify_handler(user_id, media);
    });
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StartRender(const MediaInfo& media)
{
    if (m_media_renders.find(media.media_id) != m_media_renders.end()) {
        std::cout << "Media is already being rendered: " << media.media_id << std::endl;
        return false;
    }

    RenderId render_id = m_client->GetRendererManager()->CreateRenderer(media);
    if (render_id == INVALID_RENDER_ID) {
		std::cout << "Failed to create renderer for media: " << media.media_id << std::endl;
        return false;
    }

    void* wnd = RenderManager::Instance().AddRender(render_id);
    if (!wnd) {
		std::cout << "Failed to add render for media: " << media.media_id << std::endl;
        return false;
    }

    if (!m_client->GetRendererManager()->SetRenderWindow(render_id, wnd)) {
		std::cout << "Failed to set render window for media: " << media.media_id << std::endl;
        return false;
    }

    m_media_renders.insert(std::make_pair(media.media_id, render_id));

    return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::StopRender(const MediaInfo& media)
{
    auto it = m_media_renders.find(media.media_id);
    if (it == m_media_renders.end()) {
        std::cout << "Media is not being rendered: " << media.media_id << std::endl;
        return false;
    }

    m_client->GetRendererManager()->DestroyRenderer(it->second);

    RenderManager::Instance().RemoveRender(it->second);

     m_media_renders.erase(it);

    return true;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::StopRenderAll()
{
    for (const auto& item : m_media_renders) {
        m_client->GetRendererManager()->DestroyRenderer(item.second);
        RenderManager::Instance().RemoveRender(item.second);
    }
    m_media_renders.clear();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
bool SdkManager::MakeCall(const std::string& mode, const std::string& callee)
{
    p2pms::CallMode call_mode;
    if (mode == "p2p") {
        call_mode = p2pms::CallMode::CALL_MODE_P2P;
    } else if (mode == "p2sp") {
        call_mode = p2pms::CallMode::CALL_MODE_P2SP;
    } else if (mode == "sfu") {
        call_mode = p2pms::CallMode::CALL_MODE_SFU;
    } else {
        std::cout << "invalid call mode :" << mode << std::endl;
        return false;
    }

    return m_client->MakeCall(call_mode, callee) == ERR_OK;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::TerminateCall()
{
    m_client->TerminateCall();
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnPaint(const std::string& render_id)
{
    m_client->GetRendererManager()->RefreshRender(render_id);
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::OnSizeChanged(const std::string& render_id)
{
    m_client->GetRendererManager()->OnRenderWindowSizeChanged(render_id);
}

////////////////////////////////////////////////////////////////////////////////////////////////////

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterLoginResultHandler(LoginResultHandler handler) 
{
	m_login_result_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterUserAddHandler(UserAddHandler handler) 
{
	m_user_add_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterUserRemoveHandler(UserRemoveHandler handler) 
{
	m_user_remove_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterIncomingCallHandler(IncomingCallHandler handler) 
{
	m_incoming_call_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterCallStateChangedHandler(CallStateChangedHandler handler)
{
    m_call_state_changed_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStartPublishNotifyHandler(StartPublishNotifyHandler handler) 
{
	m_start_publish_notify_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStopPublishNotifyHandler(StopPublishNotifyHandler handler) 
{
	m_stop_publish_notify_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStartSubscribeResultHandler(StartSubscribeResultHandler handler) 
{
	m_start_subscribe_result_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStopSubscribeResultHandler(StopSubscribeResultHandler handler) 
{
	m_stop_subscribe_result_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStartPublishResultHandler(StartPublishResultHandler handler) 
{
	m_start_publish_result_handler = handler;
}

//--------------------------------------------------------------------------------------------------
//
//--------------------------------------------------------------------------------------------------
void SdkManager::RegisterStopPublishResultHandler(StopPublishResultHandler handler) 
{
	m_stop_publish_result_handler = handler;
}






